using System;
using System.Threading;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Net;
using HexChessBoard;
using HexChessBoard.PlayerComponents;

namespace ScreenManagement
{
    /// <summary>
    /// This screen implements the actual game logic. It is just a
    /// placeholder to get the idea across: you'll probably want to
    /// put some more interesting gameplay in here!
    /// </summary>
    class GameplayScreen : GameScreen
    {
        #region Fields

        ContentManager content;
        SpriteFont gameFont;

        Random random = new Random();

        float pauseAlpha;

        Board board;
        GameVariant gameVariant;
        Difficulty difficulty;
        int numPlayers = 0;
        PieceColor Player1Color = PieceColor.White;
        PlayerIndex player2Index;

        Texture2D background;

        // Online Multiplayer
        NetworkSession networkSession;
        /// <summary>
        /// The logic for deciding whether the game is paused depends on whether
        /// this is a networked or single player game. If we are in a network session,
        /// we should go on updating the game even when the user tabs away from us or
        /// brings up the pause menu, because even though the local player is not
        /// responding to input, other remote players may not be paused. In single
        /// player modes, however, we want everything to pause if the game loses focus.
        /// </summary>
        new bool IsActive
        {
            get
            {
                if (networkSession == null)
                {
                    // Pause behavior for single player games.
                    return base.IsActive;
                }
                else
                {
                    // Pause behavior for networked games.
                    return !IsExiting;
                }
            }
        }

        #endregion

        #region Initialization


        /// <summary>
        /// Constructor.
        /// </summary>
        public GameplayScreen(NetworkSession p_networkSession, GameVariant selectedVariant, int p_NumPlayers, PieceColor p_Player1Color, Difficulty p_difficulty, PlayerIndex p_player2Index)
        {
            this.networkSession = p_networkSession;

            TransitionOnTime = TimeSpan.FromSeconds(0.5);
            TransitionOffTime = TimeSpan.FromSeconds(0.5);

            gameVariant = selectedVariant;
            difficulty = p_difficulty;
            numPlayers = p_NumPlayers;
            Player1Color = p_Player1Color;
            player2Index = p_player2Index;
        }


        /// <summary>
        /// Load graphics content for the game.
        /// </summary>
        public override void LoadContent()
        {
            if (content == null)
                content = new ContentManager(ScreenManager.Game.Services, "Content");

            // Load fonts
            gameFont = content.Load<SpriteFont>("Fonts/gamefont");

            // Load background
            int r = random.Next(0, 5);
            switch (r)
            {
                case 0:
                    background = content.Load<Texture2D>("Textures/ScreenManagement/stage1");
                    break;
                case 1:
                    background = content.Load<Texture2D>("Textures/ScreenManagement/stage2");
                    break;
                case 2:
                    background = content.Load<Texture2D>("Textures/ScreenManagement/stage3");
                    break;
                case 3:
                    background = content.Load<Texture2D>("Textures/ScreenManagement/stage4");
                    break;
                case 4:
                    background = content.Load<Texture2D>("Textures/ScreenManagement/stage5");
                    break;
            }

            PlayerIndex[] playerIndices = new PlayerIndex[2];
            playerIndices[0] = this.ControllingPlayer.Value;
            //if (numPlayers == 2)
            playerIndices[1] = player2Index;

            board = new Board(content, gameVariant, difficulty, numPlayers, Player1Color, new Vector2((float)this.ScreenManager.GraphicsDevice.Viewport.Width, (float)this.ScreenManager.GraphicsDevice.Viewport.Height), ScreenManager.GraphicsDevice.Viewport, playerIndices);
            Evaluation.RandomizeArrays();

            base.LoadContent();

            // once the load has finished, we use ResetElapsedTime to tell the game's
            // timing mechanism that we have just finished a very long frame, and that
            // it should not try to catch up.
            ScreenManager.Game.ResetElapsedTime();
        }


        /// <summary>
        /// Unload graphics content used by the game.
        /// </summary>
        public override void UnloadContent()
        {
            content.Unload();
        }


        #endregion

        #region Update and Draw


        /// <summary>
        /// Updates the state of the game. This method checks the GameScreen.IsActive
        /// property, so the game will stop updating when the pause menu is active,
        /// or if you tab away to a different application.
        /// </summary>
        public override void Update(GameTime gameTime, bool otherScreenHasFocus, bool coveredByOtherScreen)
        {
            base.Update(gameTime, otherScreenHasFocus, false);

            // Gradually fade in or out depending on whether we are covered by the pause screen.
            if (coveredByOtherScreen)
                pauseAlpha = Math.Min(pauseAlpha + 1f / 32, 1);
            else
                pauseAlpha = Math.Max(pauseAlpha - 1f / 32, 0);

            if (IsActive)
            {
                board.Update(gameTime);
            }

            // If we are in a network game, check if we should return to the lobby.
            if ((networkSession != null) && !IsExiting)
            {
                if (networkSession.SessionState == NetworkSessionState.Lobby)
                {
                    LoadingScreen.Load(ScreenManager, true, null, true,
                                       new BackgroundScreen(),
                                       new LobbyScreen(networkSession));
                }
            }
        }


        /// <summary>
        /// Lets the game respond to player input. Unlike the Update method,
        /// this will only be called when the gameplay screen is active.
        /// </summary>
        public override void HandleInput(InputState input, GameTime gameTime)
        {
            if (input == null)
                throw new ArgumentNullException("input");

            // Check single player (the only case to have a controlling player)
            if (ControllingPlayer.HasValue)
            {
                if (IsActive)
                {
                    if (board.Stalemate || board.WhiteCheckmate || board.BlackCheckmate || board.RepeatedMoveCount == 3 || board.FiftyMoveCount == 100)
                    {
                        if (input.IsNewKeyPress(Keys.A, ControllingPlayer) || input.IsNewButtonPress(Buttons.A, ControllingPlayer))
                        {
                            LoadingScreen.Load(ScreenManager, true, ControllingPlayer, false, new GameplayScreen(networkSession, board.Variant, board.numPlayers, board.player1Color, board.difficulty, player2Index));
                        }
                        else if (input.IsNewKeyPress(Keys.B, ControllingPlayer) || input.IsNewButtonPress(Buttons.B, ControllingPlayer))
                        {
                            board = null;
                            this.ExitScreen();
                            this.ScreenManager.AddScreen(new MainMenuScreen(), ControllingPlayer);
                        }
                    }
                    else
                        board.HandleInput(input, gameTime);
                }

                // Look up inputs for the active player profile.
                int playerIndex = (int)ControllingPlayer.Value;

                KeyboardState keyboardState = input.CurrentKeyboardStates[playerIndex];
                GamePadState gamePadState = input.CurrentGamePadStates[playerIndex];

                // The game pauses either if the user presses the pause button, or if
                // they unplug the active gamepad. This requires us to keep track of
                // whether a gamepad was ever plugged in, because we don't want to pause
                // on PC if they are playing with a keyboard and have no gamepad at all!
                bool gamePadDisconnected = !gamePadState.IsConnected && input.GamePadWasConnected[playerIndex];

                if (input.IsPauseGame(ControllingPlayer) || gamePadDisconnected)
                {
                    ScreenManager.AddScreen(new PauseMenuScreen(this.networkSession), ControllingPlayer);
                }
                else
                {
                    // if "enter" or "A", switch current turn and listen for other input
                    // OR keep both active, but only act when current turn = player.side
                }
            }
            else
            {
                // In network game modes, handle input for all the
                // local players who are participating in the session.
                foreach (LocalNetworkGamer gamer in networkSession.LocalGamers)
                {
                    if (!HandlePlayerInput(input, gamer.SignedInGamer.PlayerIndex))
                        break;
                }
            }
        }

        /// <summary>
        /// Handles input for the specified player. In local game modes, this is called
        /// just once for the controlling player. In network modes, it can be called
        /// more than once if there are multiple profiles playing on the local machine.
        /// Returns true if we should continue to handle input for subsequent players,
        /// or false if this player has paused the game.
        /// </summary>
        bool HandlePlayerInput(InputState input, PlayerIndex playerIndex)
        {
            // Look up inputs for the specified player profile.
            KeyboardState keyboardState = input.CurrentKeyboardStates[(int)playerIndex];
            GamePadState gamePadState = input.CurrentGamePadStates[(int)playerIndex];

            // The game pauses either if the user presses the pause button, or if
            // they unplug the active gamepad. This requires us to keep track of
            // whether a gamepad was ever plugged in, because we don't want to pause
            // on PC if they are playing with a keyboard and have no gamepad at all!
            bool gamePadDisconnected = !gamePadState.IsConnected &&
                                       input.GamePadWasConnected[(int)playerIndex];

            if (input.IsPauseGame(playerIndex) || gamePadDisconnected)
            {
                ScreenManager.AddScreen(new PauseMenuScreen(networkSession), playerIndex);
                return false;
            }

            return true;
        }


        /// <summary>
        /// Draws the gameplay screen.
        /// </summary>
        public override void Draw(GameTime gameTime)
        {
            // TODO move to board
            ScreenManager.GraphicsDevice.Clear(ClearOptions.Target, Color.CornflowerBlue, 0, 0);

            // TODO don't init every time
            // Draw background
            //SpriteBatch spriteBatch = ScreenManager.SpriteBatch;
            //Viewport viewport = ScreenManager.GraphicsDevice.Viewport;
            Rectangle fullscreen = new Rectangle(0, 0, ScreenManager.GraphicsDevice.Viewport.Width, ScreenManager.GraphicsDevice.Viewport.Height);

            ScreenManager.SpriteBatch.Begin();

            ScreenManager.SpriteBatch.Draw(background, fullscreen, new Color(TransitionAlpha, TransitionAlpha, TransitionAlpha));

            ScreenManager.SpriteBatch.End();

            // Draw board
            // drawing sprites changes some render states around, which don't play
            // nicely with 3d models. In particular, we want to enable the depth buffer and turn off alpha blending.
            ScreenManager.GraphicsDevice.DepthStencilState = DepthStencilState.Default;
            ScreenManager.GraphicsDevice.BlendState = BlendState.Opaque;

            if (board != null)
                board.Draw(gameTime, ScreenManager.SpriteBatch, ScreenManager.GraphicsDevice.Viewport.Width, ScreenManager.GraphicsDevice.Viewport.Height);

            if (networkSession != null)
            {
                ScreenManager.SpriteBatch.Begin();
                string message = "Players: " + networkSession.AllGamers.Count;
                Vector2 messagePosition = new Vector2(100, 480);
                ScreenManager.SpriteBatch.DrawString(gameFont, message, messagePosition, Color.White);
                ScreenManager.SpriteBatch.End();
            }

            // If the game is transitioning on or off, fade it out to black.
            if (TransitionPosition > 0 || pauseAlpha > 0)
            {
                float alpha = MathHelper.Lerp(1f - TransitionAlpha, 1f, pauseAlpha / 2);

                ScreenManager.FadeBackBufferToBlack(alpha);
            }
        }

        #endregion
    }
}
